Skip to content

chore: split order book rewards for validators#1334

Merged
MicBun merged 7 commits intomainfrom
splitValidators
Mar 12, 2026
Merged

chore: split order book rewards for validators#1334
MicBun merged 7 commits intomainfrom
splitValidators

Conversation

@MicBun
Copy link
Member

@MicBun MicBun commented Mar 11, 2026

resolves: https://github.com/trufnetwork/truf-network/issues/1380

Summary by CodeRabbit

  • New Features

    • Added endpoints to list active validators and to return the active validator count.
  • Improvements

    • Infrastructure fees are now split across all active validators; any remainder is assigned deterministically by validator ordering.
    • Fee distribution and settlement moved from leader-based to validator-centric processing, with updated audit records.
  • Tests

    • Added multi-validator distribution tests and helpers to verify payouts, remainder handling, and audit reporting.

@MicBun MicBun requested a review from pr-time-tracker March 11, 2026 11:06
@MicBun MicBun self-assigned this Mar 11, 2026
@holdex
Copy link

holdex bot commented Mar 11, 2026

Time Submission Status

Member Status Time Action Last Update
MicBun ✅ Submitted 4h 30min Update time Mar 11, 2026, 11:45 PM

You can submit time with the command. Example:

@holdex pr submit-time 15m

See available commands to help comply with our Guidelines.

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds two tn_utils precompiles (get_validators, get_validator_count), updates go.mod, rewrites order-book settlement migration to split validator rewards across active validators, and updates tests/helpers to inject and validate multiple validators.

Changes

Cohort / File(s) Summary
Precompile Addition
extensions/tn_utils/precompiles.go
Adds get_validators and get_validator_count, registers them in buildPrecompile; filters validators with power>0, sorts deterministically by pubkey bytes, and returns wallet_hex, wallet_bytes, and power.
Settlement Migration
internal/migrations/033-order-book-settlement.sql
Replaces leader-only payout with validator-centric distribution: computes per-validator share (plus remainder to first by deterministic order), unlocks per-validator funds via bridge, ensures ob_participants, and records/accumulates ob_fee_distribution_details and ob_net_impacts.
Dependency Updates
go.mod
Bumps github.com/trufnetwork/kwil-db and github.com/trufnetwork/kwil-db/core pseudo-versions.
Test Helpers
tests/streams/order_book/test_helpers_orderbook.go
Adds injectTestValidator helper to register a test validator and return its public key and derived address for deterministic ordering.
Fee Distribution Tests
tests/streams/order_book/fee_distribution_test.go, tests/streams/order_book/fee_distribution_audit_test.go
Switches tests to use injectTestValidator, adds testDistributionMultipleValidators, updates fundVaultAndDistributeFees to accept optional proposer ...crypto.PublicKey, and adjusts assertions for multi-validator distributions and audit records.
PnL Impact Test
tests/streams/order_book/pnl_impact_test.go
Injects a created user as a validator via platform.Validators.ForTestingAddValidator so settlement rewards go to that validator.

Sequence Diagram(s)

sequenceDiagram
    participant Migration as Settlement Migration
    participant Precompile as tn_utils Precompile
    participant Validators as Validators / VoteStore
    participant Bridge as Bridge Service
    participant DB as Database

    Migration->>Precompile: call get_validators()
    Precompile->>Validators: query active validators, filter power>0
    Precompile->>Precompile: sort by pubkey bytes, map to wallet_hex/wallet_bytes
    Precompile-->>Migration: return ordered validator list

    Migration->>Migration: compute per_validator = infra_share / n, remainder -> first
    loop per validator
        Migration->>Bridge: unlock per_validator funds for validator.wallet
        Migration->>DB: ensure ob_participants row exists for validator
        Migration->>DB: insert/update ob_fee_distribution_details and ob_net_impacts
        Migration->>Migration: accumulate actual_validator_fees
    end

    Migration->>DB: update ob_fee_distributions with final totals
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • pr-time-tracker

Poem

🐰 I hopped through precompiles and sorted every key,
Split infra crumbs so every validator sees,
No single leader now nibbles the loaf,
Remainder hops to first by pubkey oath,
Ledger cozy, tiny paws clap with glee.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective of distributing order book rewards to validators instead of only to the leader node.
Linked Issues check ✅ Passed The PR successfully implements validator reward distribution by adding precompile methods (get_validators, get_validator_count) and updating the settlement logic to distribute rewards across all validators proportionally.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing validator reward splitting: precompiles for validator queries, settlement logic refactoring, test infrastructure for validators, and dependency updates to support the new functionality.
Docstring Coverage ✅ Passed Docstring coverage is 95.83% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch splitValidators

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/streams/order_book/fee_distribution_test.go (1)

1170-1171: Consider using a fee amount that produces a non-zero remainder.

With totalFees = 30 TRUF, the infraShare = 3.75 TRUF divides evenly by 3 validators (1.25 TRUF each), resulting in zero remainder. The remainder-handling logic (lines 1237-1250) won't be exercised.

Consider using a fee like 31 TRUF which gives infraShare = 3.875 TRUF, producing a non-trivial remainder when divided by 3.

💡 Suggested change to test remainder handling
-		totalFees := new(big.Int).Mul(big.NewInt(30), big.NewInt(1e18))
+		// Use 31 TRUF to ensure non-zero remainder when splitting among 3 validators
+		// infraShare = 31 * 0.125 = 3.875 TRUF; 3.875 / 3 = 1.291... with remainder
+		totalFees := new(big.Int).Mul(big.NewInt(31), big.NewInt(1e18))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/streams/order_book/fee_distribution_test.go` around lines 1170 - 1171,
The test sets totalFees to 30 TRUF which yields an infraShare that divides
evenly across validators and never exercises the remainder branch; update the
totalFees initializer (variable totalFees) to use a value like 31 TRUF (e.g.,
big.NewInt(31) * 1e18) so infraShare (infraShare) produces a non-zero remainder
and the remainder-handling logic is executed during the test.
internal/migrations/033-order-book-settlement.sql (1)

118-128: Double iteration over validators for counting and distribution.

The code iterates over tn_utils.get_validators() twice: once to count validators (lines 119-121) and again to distribute (line 128). While this works correctly because the result is deterministic within a transaction, it's slightly inefficient.

This is likely a Kuneiform limitation (no way to store the result set or count during iteration). The current approach is acceptable given the constraints.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/migrations/033-order-book-settlement.sql` around lines 118 - 128,
The migration currently iterates tn_utils.get_validators() twice—once to compute
$validator_count and again to distribute $infra_share—which is inefficient;
update the logic in the block around $validator_count, $per_validator,
$val_remainder and the subsequent for loop so you only enumerate validators
once: either (a) if Kuneiform supports materializing the iterator, collect
tn_utils.get_validators() into a single in-memory list/array and derive count
and distribution from that list before distributing, or (b) add/use a helper
like tn_utils.get_validator_count() (or an equivalent COUNT query) to compute
$validator_count without a first iteration, then run the single distribution
loop over tn_utils.get_validators(); adjust references to $validator_count,
$per_validator, $val_remainder and $first_validator accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/migrations/033-order-book-settlement.sql`:
- Around line 118-128: The migration currently iterates
tn_utils.get_validators() twice—once to compute $validator_count and again to
distribute $infra_share—which is inefficient; update the logic in the block
around $validator_count, $per_validator, $val_remainder and the subsequent for
loop so you only enumerate validators once: either (a) if Kuneiform supports
materializing the iterator, collect tn_utils.get_validators() into a single
in-memory list/array and derive count and distribution from that list before
distributing, or (b) add/use a helper like tn_utils.get_validator_count() (or an
equivalent COUNT query) to compute $validator_count without a first iteration,
then run the single distribution loop over tn_utils.get_validators(); adjust
references to $validator_count, $per_validator, $val_remainder and
$first_validator accordingly.

In `@tests/streams/order_book/fee_distribution_test.go`:
- Around line 1170-1171: The test sets totalFees to 30 TRUF which yields an
infraShare that divides evenly across validators and never exercises the
remainder branch; update the totalFees initializer (variable totalFees) to use a
value like 31 TRUF (e.g., big.NewInt(31) * 1e18) so infraShare (infraShare)
produces a non-zero remainder and the remainder-handling logic is executed
during the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e60e99a8-1235-49e9-b455-85f5e379d4cb

📥 Commits

Reviewing files that changed from the base of the PR and between 78fc2b9 and 26a7b38.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • extensions/tn_utils/precompiles.go
  • go.mod
  • internal/migrations/033-order-book-settlement.sql
  • tests/streams/order_book/fee_distribution_audit_test.go
  • tests/streams/order_book/fee_distribution_test.go
  • tests/streams/order_book/pnl_impact_test.go
  • tests/streams/order_book/test_helpers_orderbook.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
tests/streams/order_book/fee_distribution_test.go (2)

1115-1118: Consider adding a test assertion for remainder recipient determinism.

The test description mentions "remainder going to the first (sorted by pubkey)" but the assertions only verify diff <= 1 without confirming which validator receives the remainder. If the specification requires deterministic remainder assignment (sorted by pubkey), consider:

  1. Sorting the injected validators by pubkey
  2. Asserting the first validator (sorted) receives more than the others

This would validate the deterministic ordering behavior described in the function comment.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/streams/order_book/fee_distribution_test.go` around lines 1115 - 1118,
The test testDistributionMultipleValidators currently only checks fee
differences <= 1; update it to also assert deterministic remainder recipient by
sorting the injected validators by their public key (use the same pubkey field
used in setup) and then assert that the first validator in that sorted order
receives the extra remainder (i.e., its received fee > each other validator's
fee, or that it equals max and at least one other is less), thereby verifying
the comment's "remainder going to the first (sorted by pubkey)" behavior.

1231-1250: Remainder handling assertions may be brittle depending on implementation.

The test asserts both diff <= 1 (line 1245) and diff > 0 (line 1249). With 31 TRUF and 3 validators:

  • infraShare = 3,875,000,000,000,000,000 wei
  • Per-validator = 1,291,666,666,666,666,666.67...

The actual diff depends on how the SQL implementation handles division:

  • Truncation + single-recipient remainder: remainder = 2 wei → diff = 2 (fails <= 1 assertion)
  • Rounding + negative correction: first validator gets 1 less → diff = 1 (passes)
  • Truncation + spread remainder 1 wei each: two validators get +1 each → diff = 1 (passes)

Consider adding a comment documenting the expected implementation behavior, or relaxing the <= 1 constraint to <= 2 if the implementation gives all remainder to one validator.

📋 Suggestion: Add implementation documentation or relax constraint
 		// Payouts should differ by at most 1 wei (remainder handling)
 		// PostgreSQL NUMERIC(78,0) division rounds, so the first validator
 		// gets per_validator + remainder (which may be negative, i.e., 1 wei less)
+		// NOTE: With infraShare=3875000000000000000 / 3, PostgreSQL banker's rounding
+		// gives per_validator=1291666666666666667, total=3875000000000000001,
+		// so remainder=-1 is applied to the first validator.
 		vals := []*big.Int{inc1, inc2, inc3}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/streams/order_book/fee_distribution_test.go` around lines 1231 - 1250,
The two assertions around validator payout differences (variables vals, minVal,
maxVal, diff and the require.True checks) are brittle given varying SQL
division/remainder strategies; update the test to allow a maximum difference of
2 wei (change the first require.True from diff ≤ 1 to diff ≤ 2) and adjust the
second assertion to assert diff ≥ 1 only when the test is meant to guarantee a
non-zero remainder, or remove the strict diff.Sign() > 0 check and instead add a
clarifying comment describing that SQL implementations may assign remainder up
to 2 wei to a single validator; ensure the change updates the message text to
reflect the new bound.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@extensions/tn_utils/precompiles.go`:
- Around line 258-271: The current Handler counts all validators with v.Power >
0 directly from app.Validators.GetValidators(), but get_validators() (the
exported precompile function) applies additional filtering (skipping malformed
secp256k1 keys); change the Handler to mirror that exact filtering by either
calling the same get_validators() helper and counting its returned slice or by
applying the same malformed-key validation used in get_validators() when
iterating validators from app.Validators.GetValidators(), and return that
filtered count instead of counting every v.Power > 0.
- Around line 203-211: The code currently calls app.Validators.GetValidators()
and then mutates the returned slice in place via sort.Slice (variable
validators), which can mutate external state; fix by making a shallow copy of
the slice returned by app.Validators.GetValidators() (e.g., validatorsCopy :=
make([]<elementType>, len(validators)); copy(validatorsCopy, validators)) and
then run sort.Slice on validatorsCopy instead of validators so the VIEW
precompile does not produce side effects.

In `@tests/streams/order_book/fee_distribution_test.go`:
- Around line 1128-1131: The test's remainder assumption is wrong: when
distributing 31 TRUF among 3 validators the remainder can be 2 wei, so update
the assertion in fee_distribution_test (around the code using
injectTestValidator and the diff comparison) to allow a difference of up to 2
wei (change big.NewInt(1) to big.NewInt(2)) or alternatively choose a test
amount divisible by 3 to keep remainder ≤1 wei; also update/ remove the
misleading comment about PostgreSQL NUMERIC rounding and ensure the check still
compares balances from the validator payout logic that uses
app.Validators/get_validators.

---

Nitpick comments:
In `@tests/streams/order_book/fee_distribution_test.go`:
- Around line 1115-1118: The test testDistributionMultipleValidators currently
only checks fee differences <= 1; update it to also assert deterministic
remainder recipient by sorting the injected validators by their public key (use
the same pubkey field used in setup) and then assert that the first validator in
that sorted order receives the extra remainder (i.e., its received fee > each
other validator's fee, or that it equals max and at least one other is less),
thereby verifying the comment's "remainder going to the first (sorted by
pubkey)" behavior.
- Around line 1231-1250: The two assertions around validator payout differences
(variables vals, minVal, maxVal, diff and the require.True checks) are brittle
given varying SQL division/remainder strategies; update the test to allow a
maximum difference of 2 wei (change the first require.True from diff ≤ 1 to diff
≤ 2) and adjust the second assertion to assert diff ≥ 1 only when the test is
meant to guarantee a non-zero remainder, or remove the strict diff.Sign() > 0
check and instead add a clarifying comment describing that SQL implementations
may assign remainder up to 2 wei to a single validator; ensure the change
updates the message text to reflect the new bound.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 68b2f492-3f60-4f26-982e-70b3beb247d6

📥 Commits

Reviewing files that changed from the base of the PR and between 26a7b38 and 20d35ba.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • extensions/tn_utils/precompiles.go
  • go.mod
  • internal/migrations/033-order-book-settlement.sql
  • tests/streams/order_book/fee_distribution_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • go.mod
  • internal/migrations/033-order-book-settlement.sql

@MicBun MicBun marked this pull request as draft March 11, 2026 14:07
@MicBun MicBun marked this pull request as ready for review March 11, 2026 15:48
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/streams/order_book/fee_distribution_audit_test.go (1)

519-534: Defensive code path that may be unreachable with current test setup.

The fallback logic (Lines 524-532) handles the case where User1 is also the validator/leader. However, since injectTestValidator always creates a fresh keypair (see context snippet 1), User1 will never be the validator in this test. This code path may never execute.

Consider either:

  1. Removing the fallback if it's dead code
  2. Adding a test case that explicitly tests User1 as the validator

This is a minor observation and doesn't affect correctness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/streams/order_book/fee_distribution_audit_test.go` around lines 519 -
534, The test contains a defensive fallback that adds the validator share to
expectedIncrease1 when increase1 mismatches auditReward1, but given
injectTestValidator always generates a new keypair User1 can never be the
validator so that branch is effectively dead; either remove the unreachable
fallback block (the if that parses totalValFeesStr and updates expectedIncrease1
using infraShareVal) from the test around variables increase1, expectedIncrease1
and auditReward1, or add an explicit test variant that makes User1 the validator
by reusing User1's keypair with injectTestValidator (so the fallback is
exercised) and assert the validator-share-adjusted expectation instead. Ensure
you update or add assertions referencing increase1.String() and
expectedIncrease1.String() accordingly.
internal/migrations/033-order-book-settlement.sql (1)

141-144: Integer division truncates $per_validator; verify remainder handling is sufficient.

The calculation $infra_share / $validator_count performs integer division, which truncates. The remainder is computed and given to the first validator. However, $val_pct is computed as 12.50 / $validator_count which also truncates, and this percentage is added per-validator without accounting for the percentage remainder. Over many validators, the summed percentages in ob_fee_distribution_details may not equal exactly 12.50%.

This is likely acceptable for audit purposes (small rounding error), but worth noting if exact percentage tracking is required.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/migrations/033-order-book-settlement.sql` around lines 141 - 144,
The current math truncates when computing per-validator shares and the
per-validator percentage ($per_validator, $val_remainder, $val_pct) causing lost
fractional percentage mass; change $val_pct to compute using a higher-precision
NUMERIC (e.g., NUMERIC(10,4) or more) so division preserves fractional cents,
then compute a percentage remainder like $pct_remainder := 12.50::NUMERIC(...) -
($val_pct * $validator_count::NUMERIC(...)) and distribute that remainder
deterministically across validators (e.g., add the smallest precision unit to
the first N validators using the existing $first_validator / loop logic) so the
sum written to ob_fee_distribution_details equals exactly 12.50 while keeping
$per_validator/$val_remainder handling consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/migrations/033-order-book-settlement.sql`:
- Around line 84-99: Migration 033 inserts into ob_fee_distributions and
ob_fee_distribution_details before those tables exist (they are created in
migration 036); fix by either moving the CREATE TABLE statements for
ob_fee_distributions and ob_fee_distribution_details from migration 036 into
migration 033 above the INSERTs, or renumbering migration 033 to run after 036
so the tables exist when INSERT INTO ob_fee_distributions and INSERT INTO
ob_fee_distribution_details execute.

---

Nitpick comments:
In `@internal/migrations/033-order-book-settlement.sql`:
- Around line 141-144: The current math truncates when computing per-validator
shares and the per-validator percentage ($per_validator, $val_remainder,
$val_pct) causing lost fractional percentage mass; change $val_pct to compute
using a higher-precision NUMERIC (e.g., NUMERIC(10,4) or more) so division
preserves fractional cents, then compute a percentage remainder like
$pct_remainder := 12.50::NUMERIC(...) - ($val_pct *
$validator_count::NUMERIC(...)) and distribute that remainder deterministically
across validators (e.g., add the smallest precision unit to the first N
validators using the existing $first_validator / loop logic) so the sum written
to ob_fee_distribution_details equals exactly 12.50 while keeping
$per_validator/$val_remainder handling consistent.

In `@tests/streams/order_book/fee_distribution_audit_test.go`:
- Around line 519-534: The test contains a defensive fallback that adds the
validator share to expectedIncrease1 when increase1 mismatches auditReward1, but
given injectTestValidator always generates a new keypair User1 can never be the
validator so that branch is effectively dead; either remove the unreachable
fallback block (the if that parses totalValFeesStr and updates expectedIncrease1
using infraShareVal) from the test around variables increase1, expectedIncrease1
and auditReward1, or add an explicit test variant that makes User1 the validator
by reusing User1's keypair with injectTestValidator (so the fallback is
exercised) and assert the validator-share-adjusted expectation instead. Ensure
you update or add assertions referencing increase1.String() and
expectedIncrease1.String() accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f0bf651-efa2-4119-af3d-7277296d4674

📥 Commits

Reviewing files that changed from the base of the PR and between 7cf1f83 and 35d2704.

📒 Files selected for processing (2)
  • internal/migrations/033-order-book-settlement.sql
  • tests/streams/order_book/fee_distribution_audit_test.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
internal/migrations/033-order-book-settlement.sql (1)

95-99: ⚠️ Potential issue | 🔴 Critical

Create the audit tables before inserting the placeholder row.

ob_fee_distributions and ob_fee_distribution_details are still defined in internal/migrations/036-order-book-audit.sql, so this insert will run before the tables exist and fail during migration replay. Move those table definitions earlier or renumber this migration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/migrations/033-order-book-settlement.sql` around lines 95 - 99, The
INSERT into ob_fee_distributions is running before the audit tables are created
(they're defined in 036-order-book-audit.sql), causing migrations to fail; fix
by ensuring ob_fee_distributions and ob_fee_distribution_details are created
before you insert the placeholder row: either move the CREATE TABLE statements
from 036-order-book-audit.sql into an earlier migration (before the INSERT in
033-order-book-settlement.sql) or renumber the migration files so that 036
becomes earlier than 033; update references to the INSERT (variables like
$distribution_id, $query_id, $block_count, `@block_timestamp`) remain unchanged.
🧹 Nitpick comments (1)
tests/streams/order_book/fee_distribution_audit_test.go (1)

594-624: Make the proposer explicit instead of silently falling back.

NewTestProposerPub(t) is not registered as a validator in this helper, so a call site that forgets to pass valPub still reaches distribute_fees, but no longer exercises the validator-aware path these tests are meant to cover. Requiring a registered proposer here would fail fast instead of silently downgrading the scenario.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/streams/order_book/fee_distribution_audit_test.go` around lines 594 -
624, The helper fundVaultAndDistributeFees currently silently generates a
proposer via NewTestProposerPub when none is passed, which masks tests that
should exercise the validator-aware path; change fundVaultAndDistributeFees to
require an explicit proposer (remove the fallback that calls NewTestProposerPub)
and return an error immediately if proposer is not supplied, and ensure callers
are updated to pass the intended validator public key; additionally before
calling distribute_fees verify the provided proposer is a registered validator
(or fail fast) so tests can't accidentally skip the validator-aware branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/migrations/033-order-book-settlement.sql`:
- Around line 233-239: The current cast to NUMERIC(10,2) for $curr_pct causes
tiny positive LP shares <0.005 to become 0.00 and then violate the constraint
ob_fee_distribution_details.total_reward_percent > 0; fix by preserving higher
precision for audit rows or skipping zero-percent inserts: stop casting
$res.reward_percent to NUMERIC(10,2) (remove/replace the $curr_pct conversion)
so the inserted/updated total_reward_percent uses a higher-precision numeric
(e.g. same scale as source) or add a guard to only INSERT/UPDATE when $curr_pct
> 0; update references to $curr_pct in the INSERT ... ON CONFLICT SET clause
accordingly.
- Around line 141-185: The loop over tn_utils.get_validators() can assign
$v_payout = 0 when $per_validator is zero, causing the INSERT/UPDATE into
ob_fee_distribution_details to violate the >0 constraint; update the validator
loop (inside the for $v in tn_utils.get_validators() block) to skip any
validator whose computed $v_payout <= 0: before calling the bridge unlock
functions (hooodi_tt2.unlock/sepolia_bridge.unlock/ethereum_bridge.unlock),
before creating/looking up participant rows, before calling
ob_record_net_impact, and before inserting/updating ob_fee_distribution_details,
add a guard that only proceeds when $v_payout > 0 (or alternatively stop after
distributing at most $infra_share smallest positive payouts), ensuring
zero-value payouts are neither unlocked nor written to
ob_fee_distribution_details.

---

Duplicate comments:
In `@internal/migrations/033-order-book-settlement.sql`:
- Around line 95-99: The INSERT into ob_fee_distributions is running before the
audit tables are created (they're defined in 036-order-book-audit.sql), causing
migrations to fail; fix by ensuring ob_fee_distributions and
ob_fee_distribution_details are created before you insert the placeholder row:
either move the CREATE TABLE statements from 036-order-book-audit.sql into an
earlier migration (before the INSERT in 033-order-book-settlement.sql) or
renumber the migration files so that 036 becomes earlier than 033; update
references to the INSERT (variables like $distribution_id, $query_id,
$block_count, `@block_timestamp`) remain unchanged.

---

Nitpick comments:
In `@tests/streams/order_book/fee_distribution_audit_test.go`:
- Around line 594-624: The helper fundVaultAndDistributeFees currently silently
generates a proposer via NewTestProposerPub when none is passed, which masks
tests that should exercise the validator-aware path; change
fundVaultAndDistributeFees to require an explicit proposer (remove the fallback
that calls NewTestProposerPub) and return an error immediately if proposer is
not supplied, and ensure callers are updated to pass the intended validator
public key; additionally before calling distribute_fees verify the provided
proposer is a registered validator (or fail fast) so tests can't accidentally
skip the validator-aware branch.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc381dd1-a4d7-4a0f-8a50-a84c199b661b

📥 Commits

Reviewing files that changed from the base of the PR and between 35d2704 and 7c62ab6.

📒 Files selected for processing (2)
  • internal/migrations/033-order-book-settlement.sql
  • tests/streams/order_book/fee_distribution_audit_test.go

@MicBun MicBun merged commit 21b1973 into main Mar 12, 2026
7 checks passed
@MicBun MicBun deleted the splitValidators branch March 12, 2026 00:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant